#include <assert.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "viawin.h"
#include "resource.h"

#ifdef _DEBUG
#include <FreeImage.h>
#endif

using namespace nVIA;

char* CGui::m_szAppName = _T("VIAssist");
WNDPROC	CGui::m_wndProc = CViaWin_WndProc;
TCHAR*	CGui::m_msgWndClassName = _T("VIAssistMSG");
WNDPROC	CGui::m_msgWndProc = CViaWin_MsgWndProc;
WNDPROC CGui::m_vsWndProc = NULL;

//////////////////////////////////////////////////////////////////////////
/// CallBack functions
//////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK CViaWin_VsWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	char c;
	switch (message) {
// 	case WM_CHAR:
// 		c = (char) wParam;
// 		if (c == 'a')
// 			wParam = 'z';
// 		else if (c == 'z')
// 			wParam = 'a';
// 		break;
 	}
	try {
		return ::CallWindowProc(CGui::m_vsWndProc, hWnd, message, wParam, lParam);
	}
	catch(LONG e){ return 0;}
	return 0;
}

LRESULT CALLBACK CViaWin_MsgWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CWin* pWin;

	switch (message) {
	case WM_VIA_UPDATE:
		pWin = (CWin*) lParam;
		if (pWin)
			if (!pWin->UpdateData())
				return 0;
			else
				return 1;
		break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

LRESULT CALLBACK CViaWin_WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	CWin* pWin = (CWin*) ::GetWindowLong(hWnd, 0);
	HWND hWnd_c;
	RECT rt;

	DWORD id = ::GetCurrentThreadId();
	
	switch (message) 
	{
	//case WM_CHAR:
	//	{
	//		char c = (char) wParam;
	//		if (c == '\n' && pWin)
	//			pWin->UpdateData();
	//		//return 0;
	//	}
	//	break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam); 
		wmEvent = HIWORD(wParam); 
		hWnd_c = (HWND) lParam;
		if (pWin)
			pWin->OnToolbar(wmId, wmEvent, hWnd_c);
		//return 0;
		break;
	case WM_CREATE:
		{
			 CREATESTRUCT* p = (CREATESTRUCT*) lParam;
			 CWin* pWin = (CWin*) p->lpCreateParams;
			 if (pWin)
				 pWin->OnCreate(hWnd);
			 SetFocus(hWnd);
			 //return 0;
		}			
		break;
	case WM_SIZE:
		{
			int cxClient = LOWORD (lParam) ;
			int cyClient = HIWORD (lParam) ;
			RECT rt = {0, 0, cxClient, cyClient};
			if (pWin)
				pWin->OnSize(&rt);
			//return 0;
		}
		break;
// 	case WM_MOUSEMOVE:
// 		{
// 			int x = LOWORD (lParam) ;
// 			int y = HIWORD (lParam) ;
// 			GetClientRect(hWnd, &rt);
// 			if (y - rt.top > 24)
// 				SetFocus(hWnd);
// 		}
// 		
// 		break;
	case WM_LBUTTONDOWN:
		{
			int x = LOWORD(lParam);
			int y = HIWORD(lParam);
			wmEvent = wParam;
			if (pWin)
				pWin->OnLButtonDown(wmEvent, x, y);
			//return 0;
		}
		
		break;
	case WM_PAINT:
		if (pWin)
			pWin->OnPaint();
		//return 0;
		break;
//	case WM_DESTROY:
		//return 0;
		//break;
	case WM_ERASEBKGND:
		return 1;
		break;
	default:
		break;
	}
   return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL CALLBACK CViaWin_DlgProc(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	CDialog* pWin = (CDialog*) ::GetWindowLong(hDlg, 0);
	DWORD e;
	LONG v;
	e = ::GetLastError();

	switch (iMsg) {
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
			//case IDCANCEL:
			//::DestroyWindow(hDlg);
			//return TRUE;
		}
		break;
	case WM_CLOSE :
		::DestroyWindow(hDlg);
		//pWin->DestroyWnd();
		return TRUE;
		break;
	case WM_INITDIALOG:
		//g_i = 0;
		v = ::GetWindowLong(hDlg, GWL_STYLE);
		v |= WS_SIZEBOX;
		::SetWindowLong(hDlg, GWL_STYLE, v);
		return TRUE;
		break;
// 	case WM_PAINT:
// 		//g_i= 1;
// 		break;
// 	case WM_SIZE:
// 		//g_i = 2;
// 		break;
	}
	return FALSE;
}

LRESULT CALLBACK CViaWin_CtlProc (HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	int i = ::GetWindowLong (hWnd, GWL_ID) ;
	CWin* pWin = (CWin*) ::GetWindowLong(hWnd, GWL_USERDATA);
	
	if (pWin) {
		switch (iMsg)
		{
		case WM_KEYDOWN :
			if (wParam == VK_TAB)
				::SetFocus(pWin->GetWnd((i +
				(GetKeyState (VK_SHIFT) < 0 ? (pWin->GetCtlNum() - 1) : 1)) % (pWin->GetCtlNum()))) ;
			break ;

		case WM_SETFOCUS :
			pWin->SetFocus(i);
			break ;
		}
	}
	return ::CallWindowProc (pWin->GetOldWndProc(i), hWnd, iMsg, wParam, lParam) ;
}

DWORD WINAPI viaDialog_Thread(PVOID pParm)
{
	MSG         msg ;
	CWin* pWin = (CWin*) pParm;

	DWORD id = ::GetCurrentThreadId();

 	if (pWin->IsDialog())
 		((CDialog*)pWin)->Create();
 	else
 		pWin->Create();

	::SetWindowPos(pWin->GetWnd(), HWND_TOP, 100, 100, INIT_WND_WIDTH, INIT_WND_HEIGHT+5, SWP_SHOWWINDOW);

	//::ShowWindow (pWin->GetWnd(), SW_SHOW) ;
	::UpdateWindow (pWin->GetWnd()) ;

	while (::GetMessage (&msg, NULL, 0, 0))
	{
		/*if (!pWin->IsDialog() || !::IsDialogMessage(pWin->GetWnd(), &msg)) */{
			::TranslateMessage (&msg) ;
			::DispatchMessage (&msg) ;
		}
	}

	::TerminateThread(pWin->m_thread, 0);
	pWin->m_threadID = 0;
	return 1;
}

DWORD WINAPI viaMsgWnd_Thread(PVOID pParm)
{
	MSG         msg ;
	CWin* pWin = (CWin*) pParm;

	//::ShowWindow (pWin->GetMsgWnd(), SW_SHOW) ;
	//::UpdateWindow (pWin->GetMsgWnd()) ;

	while (::GetMessage (&msg, NULL, 0, 0))
	{
		::TranslateMessage (&msg) ;
		::DispatchMessage (&msg) ;
	}

	::TerminateThread(pWin->m_threadMsg, 0);
	pWin->m_threadIdMsg = 0;
	return 1;
}

//////////////////////////////////////////////////////////////////////////
/// CWin
//////////////////////////////////////////////////////////////////////////

// CWin::CWin()
// {
// 	int i;
// 	for (i = 0; i < TAB_STOP_CTRL_NUM; i++)
// 		m_ctrlWndProcs[i] = NULL;
// 	m_threadID = -1;
// 	m_pData = NULL;
// 	m_toolbarHeight = 0;
// }

CWin::CWin(CGui* pGui, bool toCreate)
:m_pGui(pGui)
{
	int i;
	for (i = 0; i < TAB_STOP_CTRL_NUM; i++)
		m_ctrlWndProcs[i] = NULL;
	m_threadID = -1;
	m_pData = NULL;
	m_toolbarHeight = 0;
	m_isDialog = false;
	// don't set toCreate to be true. Why? DON'T.
 	if (toCreate)
 		Create();

	//Create(true);
}

int CWin::Create(bool isMsgWnd)
{
	if (isMsgWnd) {
		DWORD e;
		m_hMsgWnd = CreateWindow/*Ex*/(/*WS_EX_LEFT,*/ m_pGui->GetClassName(true), 
			m_pGui->GetClassName(true), WS_CAPTION, 0, 0, 1, 1, m_pGui->GetVSMainWnd(), 
			NULL, m_pGui->GetInstance(), 0);
// 		if (0 == ::SetWindowLong(m_hMsgWnd, 0, (LONG)this) && (e = ::GetLastError()) != 0)
// 			return FALSE;
		return ::SetWindowPos(m_hMsgWnd, HWND_BOTTOM, 0, 0, 1, 1, SWP_NOACTIVATE);
	}
	else {
		m_hWnd = CreateWindow/*Ex*/(/*WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_DLGMODALFRAME, */
			m_pGui->GetAppName(), m_pGui->GetAppName(),
			/*WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DLGFRAME*/
			WS_POPUPWINDOW | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX , 
			100, 100, INIT_WND_WIDTH, INIT_WND_HEIGHT,  
			m_pGui->GetVSMainWnd(),  NULL,  
			m_pGui->GetInstance(), 
			(LPVOID)this) ;	// Before CreateWindow return, WndProce will be called with
		// WM_CREATE msg. So I must pass `this' by CREATESTRUCT.

		return _Create2();
	}
}

int CWin::_Create2()
{
	LONG l, e;
	if (0 == ::SetWindowLong(m_hWnd, 0, (LONG)this) && (e = ::GetLastError()) != 0)
		return FALSE;
// 	::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, 500, 500, SWP_SHOWWINDOW);
// 	::SetLastError(0);
// 	if (0 == (l = ::GetWindowLong(m_hWnd, 0)) && (e = ::GetLastError()) != 0)
// 		return FALSE;

	if (m_pData) {
		delete m_pData;
		m_pData = NULL;
	}
	m_pData = new CData(m_pGui->GetViaCommands());
	int i = sizeof(m_editDirties);
	memset(m_editDirties, 0, sizeof(m_editDirties));
	for (i = 0; i < 4; i++)
		m_editContents[i][0] = 0;

	m_bDataDirty = FALSE;
	m_bSync = FALSE;

	return TRUE;
}

void CWin::Run()
{
	//m_threadMsg = ::CreateThread(NULL, 0, viaMsgWnd_Thread, (PVOID)this, 0, &m_threadIdMsg);
	m_thread = ::CreateThread(NULL, 0, viaDialog_Thread, (PVOID)this, 0, &m_threadID);
}

void CWin::Release()
{
	if (m_pData) {
		delete m_pData;
		m_pData = NULL;
	}
	DestroyWnd();
	if (m_threadID) {
		::TerminateThread(m_thread, 0);
		m_threadID = 0;
	}
	// todo
	// 1. How to know the thread has ended itself?
	// 2. How to know if the window is closed?
}

void	CWin::GetMetric()
{
	HDC dc = ::GetDC(m_hWnd);
	TEXTMETRIC tm;
	RECT rect;

	::GetTextMetrics(dc, &tm);
	::GetClientRect(m_hWnd, &rect);
	
	m_ctrlHeight = m_toolbarHeight = 3 * tm.tmHeight / 2;
	m_toolbarHeight += 2;
	m_childWndWidths[ID_STATIC_DATA] = tm.tmAveCharWidth * 4;
	m_childWndWidths[ID_STATIC_WIDTH] = 
		m_childWndWidths[ID_STATIC_HEIGHT] = 
		m_childWndWidths[ID_BUTTON_ALIGN] = tm.tmAveCharWidth * 8;
	m_childWndWidths[ID_EDIT_DATA] = tm.tmAveCharWidth * 18;
	m_childWndWidths[ID_EDIT_WIDTH] = 
		m_childWndWidths[ID_EDIT_HEIGHT] = tm.tmAveCharWidth * 12;
	m_childWndWidths[ID_LIST_FORMAT] = tm.tmAveCharWidth * 12;
	m_childWndWidths[ID_BUTTON_OK] = tm.tmAveCharWidth * 4;
// 	m_childWndWidths[ID_STATIC_TOOLBOX] = m_childWndWidths[ID_STATIC_DATA] * 4 +
// 		m_childWndWidths[ID_EDIT_DATA] + m_childWndWidths[ID_EDIT_WIDTH] * 3 +
// 		7;
	m_childWndWidths[ID_STATIC_TOOLBOX] = rect.right - rect.left;

	::ReleaseDC(m_hWnd, dc);
}

BOOL	CWin::OnCreate(HWND hWnd)
{
	// At this time, m_hWnd has not be initialized, so HWND must be passed from
	// outside.
	m_hChildWnds[ID_STATIC_TOOLBOX] = CreateWindow("static", NULL, 
		WS_CHILD | WS_VISIBLE | SS_GRAYRECT | SS_CENTER | COLOR_BTNFACE, 
		0, 0, 0, 0, 
		hWnd, (HMENU) ID_STATIC_TOOLBOX, 
		m_pGui->GetInstance(), NULL);
	m_hChildWnds[ID_STATIC_DATA] = CreateWindow("static", _T("bits"), 
		WS_CHILD | WS_VISIBLE | SS_CENTER, 
		0, 0, 0, 0, 
		hWnd, (HMENU) ID_STATIC_DATA, 
		m_pGui->GetInstance(), NULL);
	m_hChildWnds[ID_STATIC_WIDTH] = CreateWindow("static", _T("width"), 
		WS_CHILD | WS_VISIBLE | SS_CENTER, 
		0, 0, 0, 0, 
		hWnd, (HMENU)ID_STATIC_WIDTH, 
		m_pGui->GetInstance(), NULL);
	m_hChildWnds[ID_STATIC_HEIGHT] = CreateWindow("static", _T("height"), 
		WS_CHILD | WS_VISIBLE | SS_CENTER, 
		0, 0, 0, 0, 
		hWnd, (HMENU) ID_STATIC_HEIGHT,
		m_pGui->GetInstance(), NULL);
	//m_hChildWnds[ID_STATIC_INFO] = CreateWindow("static", _T("format"), 
	//	WS_CHILD | WS_VISIBLE | SS_CENTER, 
	//	0, 0, 0, 0, 
	//	hWnd, (HMENU) ID_STATIC_INFO,
	//	m_pGui->GetInstance(), NULL);

	int i = 0;
	for (; i < TAB_STOP_CTRL_NUM; i++) {
		if (i >= 0 && i < EDIT_NUM)
			m_hChildWnds[i] = CreateWindow (_T("edit"), NULL,
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP/* | WS_GROUP*/ |
			ES_LEFT | ES_AUTOHSCROLL,
			0, 0, 0, 0,
			hWnd, (HMENU)i,
			m_pGui->GetInstance(), NULL) ;
		if (i == ID_LIST_FORMAT)
			m_hChildWnds[i] = CreateWindow (_T("listbox"), NULL,
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP/* | WS_GROUP*/ |
			LBS_NOTIFY | WS_BORDER,
			0, 0, 0, 0,
			hWnd, (HMENU)i,
			m_pGui->GetInstance(), NULL) ;
		if (i == ID_BUTTON_OK)
			m_hChildWnds[i] = CreateWindow (_T("button"), _T("OK"),
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP/* | WS_GROUP*/ |
			BS_PUSHBUTTON,
			0, 0, 0, 0,
			hWnd, (HMENU)i,
			m_pGui->GetInstance(), NULL) ;
		if (i == ID_BUTTON_ALIGN)
			m_hChildWnds[i] = CreateWindow (_T("button"), _T("Align"),
			WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP/* | WS_GROUP*/ |
			BS_CHECKBOX,
			0, 0, 0, 0,
			hWnd, (HMENU)i,
			m_pGui->GetInstance(), NULL) ;

		m_ctrlWndProcs[i] = (WNDPROC) ::SetWindowLong(m_hChildWnds[i], GWL_WNDPROC, (LONG)CViaWin_CtlProc);
		::SetWindowLong(m_hChildWnds[i], GWL_USERDATA, (LONG)this);
	}

	for (i = 0; i < CBitmap::GetFormatNum(); i++)
		::SendMessage (m_hChildWnds[ID_LIST_FORMAT], LB_INSERTSTRING, 
		i, (LPARAM) CBitmap::GetFormatNames(i)) ;

	::SendMessage (m_hChildWnds[ID_BUTTON_ALIGN], BM_SETCHECK, 1, 0) ;

	return TRUE;
}

void 	CWin::GetChildPos(RECT* prt)
{
	int y = prt->top + 1;
	m_childWndPoses[ID_STATIC_TOOLBOX].y = prt->top;
	m_childWndPoses[ID_STATIC_WIDTH].y = y;
	m_childWndPoses[ID_STATIC_HEIGHT].y = y;
	m_childWndPoses[ID_STATIC_DATA].y = y;
	m_childWndPoses[ID_BUTTON_ALIGN].y = y;
	m_childWndPoses[ID_EDIT_DATA].y = y;
	m_childWndPoses[ID_EDIT_WIDTH].y = y;
	m_childWndPoses[ID_EDIT_HEIGHT].y = y;
	m_childWndPoses[ID_LIST_FORMAT].y = y+2;
	m_childWndPoses[ID_BUTTON_OK].y = y;

	m_childWndPoses[ID_STATIC_TOOLBOX].x = prt->left;
	m_childWndPoses[ID_STATIC_DATA].x = prt->left;
	m_childWndPoses[ID_EDIT_DATA].x = m_childWndPoses[ID_STATIC_DATA].x + m_childWndWidths[ID_STATIC_DATA] + 1;
	m_childWndPoses[ID_STATIC_WIDTH].x = m_childWndPoses[ID_EDIT_DATA].x + m_childWndWidths[ID_EDIT_DATA] + 1;
	m_childWndPoses[ID_EDIT_WIDTH].x = m_childWndPoses[ID_STATIC_WIDTH].x + m_childWndWidths[ID_STATIC_WIDTH] + 1;
	m_childWndPoses[ID_STATIC_HEIGHT].x = m_childWndPoses[ID_EDIT_WIDTH].x + m_childWndWidths[ID_EDIT_WIDTH] + 1;
	m_childWndPoses[ID_EDIT_HEIGHT].x = m_childWndPoses[ID_STATIC_HEIGHT].x + m_childWndWidths[ID_STATIC_HEIGHT] + 1;
	m_childWndPoses[ID_LIST_FORMAT].x = m_childWndPoses[ID_EDIT_HEIGHT].x + m_childWndWidths[ID_EDIT_HEIGHT] + 1;
	m_childWndPoses[ID_BUTTON_ALIGN].x = m_childWndPoses[ID_LIST_FORMAT].x + m_childWndWidths[ID_LIST_FORMAT] + 1;
	m_childWndPoses[ID_BUTTON_OK].x = m_childWndPoses[ID_BUTTON_ALIGN].x + m_childWndWidths[ID_BUTTON_ALIGN] + 1;
}

BOOL	CWin::OnSize(RECT* prt)
{
	GetMetric();
	GetChildPos(prt);

	::MoveWindow(m_hChildWnds[ID_STATIC_TOOLBOX], m_childWndPoses[ID_STATIC_TOOLBOX].x, m_childWndPoses[ID_STATIC_TOOLBOX].y,
		m_childWndWidths[ID_STATIC_TOOLBOX], m_toolbarHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_STATIC_WIDTH], m_childWndPoses[ID_STATIC_WIDTH].x, m_childWndPoses[ID_STATIC_WIDTH].y,
		m_childWndWidths[ID_STATIC_WIDTH], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_STATIC_HEIGHT], m_childWndPoses[ID_STATIC_HEIGHT].x, m_childWndPoses[ID_STATIC_HEIGHT].y,
		m_childWndWidths[ID_STATIC_HEIGHT], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_STATIC_DATA], m_childWndPoses[ID_STATIC_DATA].x, m_childWndPoses[ID_STATIC_DATA].y,
		m_childWndWidths[ID_STATIC_DATA], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_BUTTON_ALIGN], m_childWndPoses[ID_BUTTON_ALIGN].x, m_childWndPoses[ID_BUTTON_ALIGN].y,
		m_childWndWidths[ID_BUTTON_ALIGN], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_EDIT_DATA], m_childWndPoses[ID_EDIT_DATA].x, m_childWndPoses[ID_EDIT_DATA].y,
		m_childWndWidths[ID_EDIT_DATA], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_EDIT_WIDTH], m_childWndPoses[ID_EDIT_WIDTH].x, m_childWndPoses[ID_EDIT_WIDTH].y,
		m_childWndWidths[ID_EDIT_WIDTH], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_EDIT_HEIGHT], m_childWndPoses[ID_EDIT_HEIGHT].x, m_childWndPoses[ID_EDIT_HEIGHT].y,
		m_childWndWidths[ID_EDIT_HEIGHT], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_LIST_FORMAT], m_childWndPoses[ID_LIST_FORMAT].x, m_childWndPoses[ID_LIST_FORMAT].y,
		m_childWndWidths[ID_LIST_FORMAT], m_ctrlHeight, TRUE);
	::MoveWindow(m_hChildWnds[ID_BUTTON_OK], m_childWndPoses[ID_BUTTON_OK].x, m_childWndPoses[ID_BUTTON_OK].y,
		m_childWndWidths[ID_BUTTON_OK], m_ctrlHeight, TRUE);

	//::ShowWindow(m_hChildWnds[ID_EDIT_DATA], SW_SHOW);
	//::ShowWindow(m_hChildWnds[ID_EDIT_WIDTH], SW_SHOW);
	//::ShowWindow(m_hChildWnds[ID_EDIT_HEIGHT], SW_SHOW);
	//::ShowWindow(m_hChildWnds[ID_LIST_FORMAT], SW_SHOW);
	
	return TRUE;
}

BOOL	CWin::OnToolbar(int nWndId, int nNotify, HWND hWnd)
{
	assert(m_hChildWnds[nWndId] == hWnd);
	if (m_hChildWnds[nWndId] != hWnd)
		return FALSE;

	// edit control
	if (nWndId >= 0 && nWndId < EDIT_NUM) {
		switch (nNotify) {
		case EN_KILLFOCUS:
			if (m_editDirties[nWndId] != 0) {
				WORD* p = (WORD*) m_editContents[nWndId];
				*p = EDIT_LINE_BUFFER_SIZE;
				SendMessage(hWnd, EM_GETLINE, (WPARAM)0, (LPARAM)p);
				m_editDirties[nWndId] = 0;
			}
			break;
		case EN_CHANGE:
			m_editDirties[nWndId] = 1;
			break;
		case EN_UPDATE:
			m_bDataDirty = TRUE;
// 			static n = 0;
// 			n++;
// 			_stprintf(szTitle, _T("%s - %s:%d"), pWin->GetAppName(), _T("data updated"), n);
// 			SetWindowText(hWnd, szTitle);
			break;
		default:
			break;
		}
	}
	else if (nWndId == ID_LIST_FORMAT) {
		int iIndex;
		switch (nNotify) {
		case LBN_SELCHANGE:
			iIndex = ::SendMessage (hWnd, LB_GETCURSEL, 0, 0) ;
			m_pData->SetFormatIndex(iIndex);
			m_bDataDirty = TRUE;
			break;
		}
	}
	else if (nWndId == ID_BUTTON_OK) {
		if (nNotify == BN_CLICKED) {
			//::SendMessage(m_hMsgWnd, WM_VIA_UPDATE, 0, (LPARAM)this);
			if (!UpdateData())
				return FALSE;
		}
	}
	else if (nWndId == ID_BUTTON_ALIGN) {
		if (nNotify == BN_CLICKED) {
			if (m_pData->IsAlign()) {
				::SendMessage (hWnd, BM_SETCHECK, 0, 0) ;
				m_pData->EnableAlign(FALSE);

			}
			else {
				::SendMessage (hWnd, BM_SETCHECK, 1, 0) ;
				m_pData->EnableAlign();
			}
			m_bDataDirty = TRUE;
		}
	}

// 	switch (wmId)
// 	{
// 	case ID_EDIT_DATA:
// 		//_stprintf(szTitle, _T("%s - %s"), pWin->GetAppName(), _T("edit data"));
// 		//SetWindowText(hWnd, szTitle);
// 		switch (wmEvent) {
// 			
// 		case EN_KILLFOCUS:
// 			SetFocus(hWnd);
// 			break;
// 		case EN_UPDATE:
// 			static n = 0;
// 			n++;
// 			_stprintf(szTitle, _T("%s - %s:%d"), pWin->GetAppName(), _T("data updated"), n);
// 			SetWindowText(hWnd, szTitle);
// 			break;
// 		}
// 		break;
// 		case ID_EDIT_WIDTH:
// 			switch (wmEvent) {
// 			case EN_KILLFOCUS:
// 				SetFocus(hWnd);
// 				break;
// 			case EN_UPDATE:
// 				_stprintf(szTitle, _T("%s - %s"), pWin->GetAppName(), _T("width updated"));
// 				SetWindowText(hWnd, szTitle);
// 				break;
// 			}
// 			break;
// 			case ID_EDIT_HEIGHT:
// 				switch (wmEvent) {
// 				case EN_KILLFOCUS:
// 					SetFocus(hWnd);
// 					break;
// 				case EN_UPDATE:
// 					_stprintf(szTitle, _T("%s - %s"), pWin->GetAppName(), _T("height updated"));
// 					SetWindowText(hWnd, szTitle);
// 					break;
// 				}
// 				break;
// 				default:
// 					break;
// 	}

	return TRUE;
}

BOOL	CWin::OnLButtonDown(int nNotify, int x, int y)
{
	switch (nNotify) {
	case MK_LBUTTON:
		if (y > m_toolbarHeight) {
			::SetFocus(m_hWnd);
			//if (!UpdateData())
			//	return FALSE;
		}
		break;
	}
	return TRUE;
}

BOOL	CWin::UpdateData()
{
	BOOL re = FALSE;	
	int i;
#ifdef UNICODE
	static char buf[EDIT_LINE_BUFFER_SIZE * 2];
#endif

	if (!m_bDataDirty)
		return TRUE;	// do nothing, return true

	for (i = 0; i < EDIT_NUM; i++) {
		if (_tcslen(m_editContents[i]) <= 0)
			return TRUE;	// do nothing, return true
	}

#ifdef UNICODE
	if (0 == wcstombs(buf, m_editContents[ID_EDIT_WIDTH], EDIT_LINE_BUFFER_SIZE)) {
		if (!m_pData->SetWidth(buf))
			return FALSE;
	}
	else
		return FALSE;
	if (0 == wcstombs(buf, m_editContents[ID_EDIT_HEIGHT], EDIT_LINE_BUFFER_SIZE)) {
		if (!m_pData->SetHeight(buf))
			return FALSE;
	}
	else
		return FALSE;
	if (0 == wcstombs(buf, m_editContents[ID_LIST_FORMAT], EDIT_LINE_BUFFER_SIZE)) {
		if (!m_pData->SetPitch(buf))
			return FALSE;
	}
	else
		return FALSE;
	if (0 == wcstombs(buf, m_editContents[ID_EDIT_DATA], EDIT_LINE_BUFFER_SIZE)) {
		if (!m_pData->SetBits(buf))
			return FALSE;
	}
	else
		return FALSE;
#else
	if (!m_pData->SetWidth(m_editContents[ID_EDIT_WIDTH]))
		goto exit;
	if (!m_pData->SetHeight(m_editContents[ID_EDIT_HEIGHT]))
		goto exit;
// 	if (!m_pData->SetPitch(m_editContents[ID_LIST_FORMAT]))
// 		return FALSE;
	if (!m_pData->SetBits(m_editContents[ID_EDIT_DATA]))
		goto exit;
#endif

// 	BOOL iCheck = (int) ::SendMessage (m_hChildWnds[ID_BUTTON_ALIGN], BM_GETCHECK, 0, 0) ;
// 	if (iCheck)
// 		m_pData->EnableAlign();
// 	else
// 		m_pData->EnableAlign(FALSE);

	if (m_pData->UpdateData()) {
		m_bDataDirty = FALSE;
		m_bSync = TRUE;

#ifdef _DEBUG
		int width = m_pData->GetWidth();
		int height = m_pData->GetHeight();
		int pitch_s = m_pData->GetPitch();
		PBYTE bits_s = m_pData->GetBits();
		FIBITMAP* dib = FreeImage_Allocate(m_pData->GetWidth(), m_pData->GetHeight(), 24);
		PBYTE bits = FreeImage_GetBits(dib);
		int pitch = FreeImage_GetPitch(dib);
		int y;

		for (y = 0; y < height; y++) {
			memcpy(bits, bits_s, 3 * width);
			bits += pitch;
			bits_s += pitch_s;
		}
		FreeImage_Save(FIF_BMP, dib, "viadebug.bmp");
		FreeImage_Unload(dib);
#endif

		re = TRUE;
	}
	
exit:
	if (!re)
		m_bSync = FALSE;
	RECT rect;
	::GetClientRect (m_hWnd, &rect) ;
	rect.top += m_toolbarHeight;
	::InvalidateRect(m_hWnd, &rect, TRUE);
	return re;
}

BOOL	CWin::OnPaint()
{
	HDC         hdc, hdcMem, workdc;
	PAINTSTRUCT ps ;
	RECT        rect;
	BITMAP bitmap  ;
	static HBITMAP hBitmap;
	BYTE	*bits ;
	HBRUSH bsh;
	int cxClient, cyClient;

	if (!m_bDataDirty && m_bSync) {
		hdc = ::BeginPaint (m_hWnd, &ps) ;
		::GetClientRect (m_hWnd, &rect) ;
		rect.top += m_toolbarHeight;
		cxClient = rect.right - rect.left;
		cyClient = rect.bottom - rect.top;
#if 0
		hdcMem = ::CreateCompatibleDC (NULL);
		bsh = (HBRUSH) ::GetStockObject(BLACK_BRUSH);
		::FillRect(hdcMem, &rect, bsh);
		//hBitmap = ::CreateCompatibleBitmap(hdc, m_pData->GetWidth(), m_pData->GetHeight());
		//::SetBitmapBits (hBitmap, m_pData->GetPitch() * m_pData->GetHeight(), m_pData->GetBits()) ;
		hBitmap = ::CreateBitmap(m_pData->GetWidth(), m_pData->GetHeight(), 3, 8, m_pData->GetBits());
		long e = GetLastError();
		//::SelectObject(hdcMem, hBitmap);
		HBITMAP hbmT = (HBITMAP) ::SelectObject(hdcMem, hBitmap);
		BITMAP bm;
		GetObject(hBitmap, sizeof(bm), &bm);
		//::BitBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, 0, 0, SRCCOPY);
		::StretchBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
		e = GetLastError();

		::SelectObject(hdcMem, hbmT);
		::DeleteDC(hdcMem);
#endif
		::SetStretchBltMode (hdc, COLORONCOLOR) ;
		::StretchDIBits(hdc, rect.left, rect.top, cxClient, cyClient, 0, 0, 
			m_pData->GetWidth(), m_pData->GetHeight(), m_pData->GetBits(), 
			m_pData->GetInfo(), DIB_RGB_COLORS, SRCCOPY);
		::EndPaint (m_hWnd, &ps) ;
		//::DeleteObject(hBitmap);

		return TRUE;
	}
	else {
		hdc = ::BeginPaint(m_hWnd, &ps);
		// TODO: Add any drawing code here...
		
		::GetClientRect (m_hWnd, &rect) ;
		rect.top -= m_toolbarHeight;
		bsh = (HBRUSH) ::GetStockObject(BLACK_BRUSH);
		::FillRect(hdc, &rect, bsh);
		
		//viaEvaluateStr(g_hViaCommands, NULL, szExp, 1024);
		DrawText(hdc, _T("hello, vcimassist"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
		EndPaint(m_hWnd, &ps);
	}

	return FALSE;
}

BOOL	CWin::OnDestroy()
{
	//PostMessage(m_hWnd, WM_QUIT, 0, 0);
	//SendMessage(m_hWnd, WM_QUIT, 0, 0);
	m_hWnd = NULL;
	return TRUE;
}

void	CWin::DestroyWnd()
{
	if (m_hWnd) {
		::DestroyWindow(m_hWnd);
		m_hWnd = NULL;
	}
}

//////////////////////////////////////////////////////////////////////////
/// CDialog
//////////////////////////////////////////////////////////////////////////
CDialog::CDialog(CGui* pGui)
:CWin(pGui, false)
{
	CWin::m_isDialog = true;
	//Create();
}

CDialog::~CDialog()
{
}

int CDialog::Create()
{
	HWND h = ::CreateDialogParam(CWin::m_pGui->GetInstance(), MAKEINTRESOURCE(ID_VIA_DLG), 0, 
		CViaWin_DlgProc, (LPARAM)this);
	CWin::_SetWnd(h);
	return CWin::_Create2();
}

void CDialog::Run()
{
	//::ShowWindow(((CWin*)this)->GetWnd(), SW_SHOW);
	CWin::Run();
}

//////////////////////////////////////////////////////////////////////////
/// CGui
//////////////////////////////////////////////////////////////////////////

CGui::CGui(HINSTANCE hInstance, HANDLE hViaCommands, bool useDialog)
{
	m_useDialog = useDialog;
	Create(hInstance, hViaCommands);
}

CGui::~CGui()
{
	Release();
}

int CGui::Create(HINSTANCE hInstance, HANDLE hViaCommands)
{
	if (hInstance == NULL)
		m_hInstance = GetModuleHandle("viagui.dll");
	else
		m_hInstance = hInstance;
	// if set to NULL, there may be name conflictation.
//	m_hInstance = NULL;
	if (!m_useDialog)
		RegisterClass();
	RegisterClass(true);
	m_nWin = 0;

	m_hViaCommands = (HANDLE) hViaCommands;

	//::SetLastError(0);
	//m_vsWndProc = (WNDPROC) ::GetWindowLongPtr(GetVSMainWnd(), GWLP_WNDPROC);
	//(WNDPROC)::SetWindowLong(GetVSMainWnd(), GWL_WNDPROC, (LONG)CViaWin_VsWndProc);

	DWORD e = ::GetLastError();
	return TRUE;
}

void CGui::Release()
{
	int i = 0;
	for (; i < m_nWin; i++) {
		if (m_useDialog)
			delete (CDialog*)(m_lstWin[i]);
		else
			delete m_lstWin[i];
	}
	m_nWin = 0;
}

int CGui::Dialog()
{
	if (m_nWin < VIA_DIALOG_MAX_NUM) {
		if (m_useDialog) {
			m_lstWin[m_nWin++] = (CWin*)new CDialog(this);
			((CDialog*)m_lstWin[m_nWin - 1])->Run();
		}
		else {
			m_lstWin[m_nWin++] = new CWin(this);
			m_lstWin[m_nWin - 1]->Run();
		}
		return TRUE;
	}
	return FALSE;
}

ATOM CGui::RegisterClass(bool isMsgWnd)
{	
	if (isMsgWnd) {
		m_msgWndClass.cbSize        = sizeof (m_msgWndClass) ;
		m_msgWndClass.style         = CS_HREDRAW | CS_VREDRAW;
		m_msgWndClass.lpfnWndProc   = m_msgWndProc ;
		m_msgWndClass.cbClsExtra    = 0 ;
		m_msgWndClass.cbWndExtra    = sizeof(LONG);
		m_msgWndClass.hInstance     = m_hInstance;
		m_msgWndClass.hIcon         = ::LoadIcon (NULL, IDI_APPLICATION) ;
		m_msgWndClass.hCursor       = ::LoadCursor (NULL, IDC_ARROW) ;
		m_msgWndClass.hbrBackground = (HBRUSH) ::GetStockObject (BLACK_BRUSH) ;
		m_msgWndClass.lpszMenuName  = NULL ;
		m_msgWndClass.lpszClassName = m_msgWndClassName ;
		m_msgWndClass.hIconSm       = ::LoadIcon (NULL, IDI_APPLICATION) ;

		return m_WndClass = ::RegisterClassEx (&m_msgWndClass) ;
	}
	else {
		m_wndclass.cbSize        = sizeof (m_wndclass) ;
		m_wndclass.style         = CS_HREDRAW | CS_VREDRAW;
		m_wndclass.lpfnWndProc   = m_wndProc ;
		m_wndclass.cbClsExtra    = 0 ;
		m_wndclass.cbWndExtra    = sizeof(LONG);
		m_wndclass.hInstance     = m_hInstance;
		m_wndclass.hIcon         = ::LoadIcon (NULL, IDI_APPLICATION) ;
		m_wndclass.hCursor       = ::LoadCursor (NULL, IDC_ARROW) ;
		m_wndclass.hbrBackground = (HBRUSH) ::GetStockObject (BLACK_BRUSH) ;
		m_wndclass.lpszMenuName  = NULL ;
		m_wndclass.lpszClassName = m_szAppName ;
		m_wndclass.hIconSm       = ::LoadIcon (NULL, IDI_APPLICATION) ;

		return m_WndClass = ::RegisterClassEx (&m_wndclass) ;
	}
}

//////////////////////////////////////////////////////////////////////////
/// TLS functions
//////////////////////////////////////////////////////////////////////////
//extern DWORD g_dwViaTlsIndex;

/// Should be called inside thread function
//CData**	nVIA::GetTlsDataEx()
//{
// 	LPVOID lpvData; 
// 	
// 	lpvData = TlsGetValue(g_dwViaTlsIndex); 
// 	if (lpvData == NULL) {
// 		if (GetLastError() != ERROR_SUCCESS)
// 			return NULL;
// 		else {
// 			lpvData = (LPVOID) LocalAlloc(LPTR, sizeof(PVOID)); 
// 			if (lpvData == NULL) 
// 				return NULL;
// 			if (!TlsSetValue(g_dwViaTlsIndex, lpvData))
// 				return NULL;
// 		}
// 	} 
// 
// 	return (CData**) lpvData;
//	return 0;
//}

//CData**	nVIA::GetTlsData()
//{
//	return (CData**) TlsGetValue(g_dwViaTlsIndex); 
//	return 0;
//}

//////////////////////////////////////////////////////////////////////////
/// CData
//////////////////////////////////////////////////////////////////////////
BOOL	CData::getValue(char* expression, long* value)
{
	return viaCommandsEvaluate(m_hViaCommands, expression, value, VIA_LONG);
}

BOOL	CData::getPointer(char* expression, PBYTE* pt)
{
	return viaCommandsEvaluate(m_hViaCommands, expression, pt, VIA_PVOID);
}

BOOL	CData::UpdateData()
{
	if (!m_pBitmap) {
		m_pBitmap = new CBitmap(m_width, m_height, (ColorFormat)m_formatIndex, m_isAlign);
	}
	else {
		m_pBitmap->Init(m_width, m_height, (ColorFormat)m_formatIndex, m_isAlign);
	}

	if (viaCommandsReadMemory(m_hViaCommands, m_pBitmap->Buffer(), m_bits, 
		m_height * m_pBitmap->BufferPitch())) {
			m_bHasImage = m_pBitmap->Update();
			return m_bHasImage;
	}
	else {
		return m_bHasImage = FALSE;
	}
}
